#include "ListTests.h"
#include "../TestFramework/TestList.h"
#include <list>
#include <iostream>

namespace
{
	struct TestObject
    {
        TestObject()
            : A(99)
            , B(1)
        {}

        int A;
        double B;
    };
	
	TEST_CASE(TestPushBack)
    {
        TestFramework::TestList<int> list;

        for (int i = 0; i < 1000; ++i)
        {
            list.PushBack(i);
        }

        for (int i = 0; i < 1000; ++i)
        {
            CHECK(list[i] == i);
        }
    }

    TEST_CASE(TestPushBack2)
    {
        TestFramework::TestList<char*> list;

        list.PushBack("privet");
        list.PushBack("eee");
        list.PushBack("aaa");

        CHECK(list[0] == "privet");
        CHECK(list[1] == "eee");
        CHECK(list[2] == "aaa");
    }

    TEST_CASE(TestPushFront)
    {
        TestFramework::TestList<int> list;

        for (int i = 0; i < 1000; ++i)
        {
            list.PushFront(i);
        }

        for (int i = 0, j = 999; i < 1000, j >= 1; ++i, --j)
        {
            CHECK(list[i] == j);
        }
    }

    TEST_CASE(TestInsert)
    {
        TestFramework::TestList<int> list;

        for (int i = 0; i < 1000; ++i)
        {
            list.Insert(0, i);
        }

        CHECK(list[0] == 0);

        for (int i = 1, j = 999; i < 1000, j >= 1; ++i, --j)
        {
            CHECK(list[i] == j);
        }
    }

    TEST_CASE(TestMixAddition)
    {
        TestFramework::TestList<int> list;

        list.PushFront(1);
        list.PushBack(2);
        list.PushFront(3);
        list.PushFront(4);
        list.Insert(2, 5);
        list.PushFront(6);

        CHECK(list[0] == 6);
        CHECK(list[1] == 4);
        CHECK(list[2] == 3);
        CHECK(list[3] == 1);
        CHECK(list[4] == 5);
        CHECK(list[5] == 2);
    }

    TEST_CASE(TestChange)
    {
        TestFramework::TestList<int> list;

        for (int i = 0; i < 10; ++i)
        {
            list.PushBack(i);
        }

        list[0] = 20;
        list[5] = 55;

        CHECK(list[0] == 20);
        CHECK(list[1] == 1);
        CHECK(list[3] == 3);
        CHECK(list[5] == 55);
        CHECK(list[9] == 9);
    }

    TEST_CASE(TestListCheckOutOfRange)
    {
        TestFramework::TestList<int> list;

        CHECK_EXCEPTION_OCCURRENCE(list[1] = 10);

        for (int i = 1; i <= 10; ++i)
        {
            list.PushFront(i);
        }

        CHECK_EXCEPTION_OCCURRENCE(list[11] = 11);
    }

    TEST_CASE(TestListSizeGetting)
    {
        TestFramework::TestList<int> list;
        list.PushBack(1);
        list.PushFront(2);
        list.Insert(1, 3);
        CHECK(list.GetSize() == 3);
    }

    TEST_CASE(TestListIterator)
    {
        TestFramework::TestList<int> list;
        TestFramework::TestList<int>::Iterator iter;

        for (int i = 1; i <= 10; ++i)
        {
            list.PushBack(i);
        }

        int num1 = list.Begin()->Value;
        int num2 = list.End()->Value;

        CHECK(num1 == 1);
        CHECK(num2 == 10);

        iter = list.Begin();

        iter++;
        ++iter;
        
        //int num3 = iter->data;
        //CHECK(iter->data == 3);
    }

	PERFORMANCE_TEST_CASE(TestListPerformance)
    {
        START_ETALON
            std::list<TestObject> etalon;
			for (int i = 0; i < 10000; ++i)
			{
				etalon.push_back(TestObject());
			}

			for (int i = 0; i < 10000; ++i)
			{
				etalon.push_front(TestObject());
			}

			std::list<TestObject>::iterator iter1, iter2, iter3;
			iter1 = iter2 = iter3 = etalon.begin();
			for (int i = 0; i < 1000; ++i, ++iter1);
			for (int i = 0; i < 2000; ++i, ++iter2);
			for (int i = 0; i < 3000; ++i, ++iter3);

			etalon.insert(iter1, *iter2);
			//etalon.clear();

        FINISH_ETALON

        START_TEST
            TestFramework::TestList<TestObject> test;
			for (int i = 0; i < 10000; ++i)
			{
				test.PushBack(TestObject());
			}

			for (int i = 0; i < 10000; ++i)
			{
				test.PushFront(TestObject());
			}

			TestFramework::TestList<TestObject>::Iterator iter4, iter5, iter6;
			iter4 = iter5 = iter6 = test.Begin();
			for (int i = 0; i < 1000; ++i, ++iter4);
			for (int i = 0; i < 2000; ++i, ++iter5);
			for (int i = 0; i < 3000; ++i, ++iter6);

			test.Insert(3000, iter5->Value);
			//test.Clear();

        FINISH_TEST
		std::cout << std::endl << "etalon: " << EtalonFinish - EtalonStart << std::endl;
		std::cout << std::endl << "test: " << TestFinish - TestStart << std::endl;
        CHECK_GREATER_PERFORMANCE
    }

    START_TEST_SUITE(ListTest, "List test")
    {
        REGISTER_TEST(TestPushBack);
        REGISTER_TEST(TestPushBack2);
        REGISTER_TEST(TestPushFront);
        REGISTER_TEST(TestInsert);
        REGISTER_TEST(TestMixAddition);
        REGISTER_TEST(TestChange);
        REGISTER_TEST(TestListCheckOutOfRange);
        REGISTER_TEST(TestListSizeGetting);
        REGISTER_TEST(TestListIterator);
		REGISTER_TEST(TestListPerformance);
    }
    FINISH_TEST_SUITE
}

namespace Tests
{
    TestFramework::UnitTestPtr CreateListTester()
    {
        CREATE_SUITE(ListTest);
    }
}
